﻿using MsTestUtilities.RowTests;
using MsTestUtilities.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MsTestUtilities
{
    public class ExceptionExpectation<T> : IExpectation
        where T : Exception
    {
        private Predicate<string> _messagePredicate = _ => true;
        private Predicate<T> _exceptionPredicate = _ => true;
        private Func<string, string> _messageErrorGenerator;

        public ExceptionExpectation()
        {
        }

        public ExceptionExpectation(string expectedMessage)
        {
            _messagePredicate = actualMessage => expectedMessage.Equals(actualMessage);
            _messageErrorGenerator = actual => MessageEqualityError(expectedMessage, actual);
        }

        public ExceptionExpectation(Predicate<T> exceptionPredicate)
        {
            _exceptionPredicate = exceptionPredicate;            
        }

        public AssertionResult IsSatisfiedFor(Action action)
        {
            var actualException = ExceptionAnalyzer.GetException(action);
            
            if (actualException == null || actualException.GetType() != typeof(T))
            {
                return AssertionResult.Error(TypeErrorGenerator(actualException));
            }

            if (!_exceptionPredicate(actualException as T))
            {
                return AssertionResult.Error("Predicate did not hold for exception {0}", actualException);
            }
            
            if (!_messagePredicate(actualException.Message))
            {
                return AssertionResult.Error(_messageErrorGenerator(actualException.Message));
            }

            return AssertionResult.Success;            
        }

        private string MessageEqualityError(string expected, string actual)
        {
            return string.Format("Expected exception with message <{0}>. Actual message <{1}>.", expected, actual);
        }

        private string TypeErrorGenerator(Exception actualException)            
        {
            if (actualException == null)
            {
                return string.Format(
                    "Expected exception <{0}>. No excpetion was thrown.",
                    typeof(T).Name);
            }
            else if (actualException.GetType() != typeof(T))
            {
                return string.Format(
                    "Expected exception <{0}>. Actual <{1}>.",
                    typeof(T).Name,
                    actualException.GetType().Name);
            }

            return null;
        }
    }
}
